﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using System.Reflection;
using System.Xaml;
using System.ComponentModel;

namespace NeuroSpeech.XamlExtensions
{
    public class XamlTypeGenerator
    {

        public virtual CodeStatement Assign(XamlCodeContext context, CodeExpression target, CodeExpression value) {
            return new CodeAssignStatement
            {
                Left = target,
                Right = value
            };
        }


        public virtual bool RequiresCasting(XamlCodeContext context)
        {
            return context.Peek.RequiresCasting(context);
        }

        public virtual bool CanProcess(
             XamlCodeContext context
             )
        {
            return false;
        }

        public virtual void StartObject(XamlCodeContext context)
        {
            XamlItem item = context.Peek;
            XamlType type = item.Type;
            if (item.IsRoot)
                return; 
            context.PreStatements.Add(new CodeVariableDeclarationStatement
            {
                Name = item.Variable,
                Type = new CodeTypeReference(type.UnderlyingType),
                InitExpression = new CodeObjectCreateExpression(type.UnderlyingType)
            });
        }


        #region Method Transform
        public virtual CodeExpression Transform(Type targetType, object value)
        {
            CodeExpression ce = value as CodeExpression;
            if (ce != null)
                return ce;
            if (value != null && targetType != null && targetType.IsEnum)
            {
                return new CodePropertyReferenceExpression
                {
                    PropertyName = value as string,
                    TargetObject = new CodeTypeReferenceExpression(targetType)
                };
            }
            return new CodePrimitiveExpression(value);
        }
        #endregion


        #region private void TransformToType(Type type,CodeExpression value)
        public CodeExpression TransformToType(Type type, CodeExpression value)
        {
            CodePrimitiveExpression pr = value as CodePrimitiveExpression;
            if (pr != null)
            {
                object val = pr.Value;
                if (val == null)
                {
                    return pr;
                }

                return ConvertValue(type, val);

            }

            CodeMethodInvokeExpression mi = new CodeMethodInvokeExpression
            {
                Method = new CodeMethodReferenceExpression
                {
                    TargetObject = new CodeTypeReferenceExpression(typeof(Convert)),
                    MethodName = "ChangeType"
                }
            };
            mi.Parameters.Add(value);
            mi.Parameters.Add(new CodeTypeReferenceExpression(type));
            // apply type conversion expression...
            CodeCastExpression ces = new CodeCastExpression
            {
                TargetType = new CodeTypeReference(type),
                Expression = mi
            };
            return ces;
        }

        protected virtual CodeExpression ConvertValue(Type type, object val)
        {
            string strValue = val.ToString();

            PropertyInfo p = type.GetProperty(strValue, BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
            if (p != null)
            {
                return new CodePropertyReferenceExpression
                {
                    TargetObject = new CodeTypeReferenceExpression(p.DeclaringType),
                    PropertyName = p.Name
                };
            }

            if (strValue == "Auto" && type == typeof(double))
                return new CodePrimitiveExpression(double.NaN);

            // check if single or more value constructor accepting primitive type
            CodeExpression exp = GetConstructorExpression(type, strValue);
            if (exp != null)
                return exp;


            // pickup type converter if available...
            val = Convert.ChangeType(val, type);
            return new CodePrimitiveExpression(val);
        }

        #region private CodeExpression GetConstructorExpression(Type type,string strValue)
        private CodeExpression GetConstructorExpression(Type type, string strValue)
        {

            List<double> values = new List<double>();
            string[] tokens = strValue.Split(',');
            double d;
            foreach (string token in tokens) {
                if (!double.TryParse(token, out d))
                    return null;
                values.Add(d);
            }

            ConstructorInfo[] cis = type.GetConstructors();

            foreach (ConstructorInfo ci in cis)
            {
                ParameterInfo[] ps = ci.GetParameters();
                if (ps.Length != values.Count)
                    continue;
                if (ps.All(x => x.ParameterType.IsValueType)) {
                    CodeObjectCreateExpression cc = new CodeObjectCreateExpression { 
                        CreateType = new CodeTypeReference(type)
                    };
                    foreach (double v in values) {
                        cc.Parameters.Add(new CodePrimitiveExpression(v));
                    }
                    return cc;
                }
            }

            return null;
        }
        #endregion

        #endregion

    }
}
